
#ifndef ROPUFU_MULTI_SENSOR_STATISTIC_ORACLE_CUSUM_HPP_INCLUDED
#define ROPUFU_MULTI_SENSOR_STATISTIC_ORACLE_CUSUM_HPP_INCLUDED

#include "../information/information_none.hpp"
#include "../more_math.hpp"
#include "../window/window_fixed.hpp"
#include "../window/window_none.hpp"
#include "cusum.hpp"

#include <array>
#include <cmath>
#include <cstdint>
#include <ostream>
#include <string>
#include <type_traits>
#include <vector>

namespace ropufu
{
    namespace statistic
    {
        // WARNING: Only implemented for independent streams.
        template <std::size_t t_count_streams, std::size_t t_count_affected>
        struct oracle_cusum
        {
            static const std::size_t count_streams = t_count_streams;
            static const std::size_t count_affected = t_count_affected;
            typedef oracle_cusum<count_streams, count_affected> type;

        private:
            std::array<bool, count_streams> m_affected_subset_mask;
            cusum m_oracle_statistic;
            std::array<double, count_streams> m_missing;

            oracle_cusum() = delete;

        public:
            explicit oracle_cusum(const std::array<bool, count_streams>& affected_subset_mask)
                : m_affected_subset_mask(affected_subset_mask), m_oracle_statistic(), m_missing()
            {
                this->reset();
            }

            bool has_reset() const
            {
                return false;
            }

            void reset()
            {
                this->m_oracle_statistic.reset();
            }

            double observe(const std::array<double, count_streams>& instantaneous_loglikelihood)
            {
                double subset_loglikelihood = 0.0;
                for (std::size_t k = 0; k < count_streams; k++)
                {
                    // WARNING: This is where independence kicks in.
                    if (this->m_affected_subset_mask[k]) subset_loglikelihood += instantaneous_loglikelihood[k];
                }
                return this->m_oracle_statistic.observe(subset_loglikelihood);
            }

            // Returns the complete loglikelihood ratio.
            const std::array<double, count_streams>& complete() const
            {
                return this->m_missing;
            }

            // Returns the accumulated loglikelihood ratio.
            const std::array<double, count_streams>& accumulated() const
            {
                return this->m_missing;
            }

            friend std::ostream& operator <<(std::ostream& os, const type& that)
            {
                return os << "oracle_cusum_" << count_affected;
            }
        };
    }
}

#endif // ROPUFU_MULTI_SENSOR_STATISTIC_ORACLE_CUSUM_HPP_INCLUDED
